/** @file  fim.c
 *  @brief fim
 *
 */

/*
//|----------------------------------------------------------------------------
//|                SKY NETWORKS CONFIDENTIAL - COPYRIGHT 2016
//|----------------------------------------------------------------------------
//| The information contained herein is the property of Sky Networks and is
//| strictly confidential. Except as expressly authorized in writing by Sky
//| Networks, the holder shall keep all information contained herein confiden-
//| tial, shall disclose it only to its employees with a need to know, and
//| shall protect it, in whole or in part, from disclosure and dissemination
//| to third parties with the same degree of care it uses to protect its own
//| confidential information of like nature, but with no less than reasonable
//| care. Except expressly authorized in writing by Sky Networks, the holder
//| is granted no rights to use the information contained herein.
//|----------------------------------------------------------------------------
*/

/*
//|----------------------------------------------------------------------------
//| Flexelint
//|----------------------------------------------------------------------------
*/
/*lint -save */
/*lint +libclass(all) */

/*
//|----------------------------------------------------------------------------
//| Includes
//|----------------------------------------------------------------------------
*/
#include <time.h>
#include <pthread.h>
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <syslog.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/un.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <stdbool.h>
#include <argp.h>
#include <signal.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

#include "main.h"
#include "fim.h"

/*
//|----------------------------------------------------------------------------
//| Defines
//|----------------------------------------------------------------------------
*/
#ifndef MAX
#define MAX(a,b) ((a>b)?a:b)
#endif

#define VERSION_MAJOR 0
#define VERSION_MINOR 3

/*
//|----------------------------------------------------------------------------
//| Local variables
//|----------------------------------------------------------------------------
*/
static struct argp_option fim_argp_options[] =
{
    MAIN_ARGP_OPTIONS,
    {"status",     MAIN_ARG_USER_FIRST_KEY,   0,   0,    "status", 0},
    {"terminate",  MAIN_ARG_USER_FIRST_KEY+1, 0,   0,    "terminate", 0},
    {"file",       MAIN_ARG_USER_FIRST_KEY+2, "FILE", 0, "configuration file", 0},
    {"start_level",MAIN_ARG_USER_FIRST_KEY+3, "SLEVEL", 0,"start level", 0},
    {"run_level",  MAIN_ARG_USER_FIRST_KEY+4, "RLEVEL", 0, "run level", 0},
    {"dontkill",   MAIN_ARG_USER_FIRST_KEY+5, 0, 0, "dont kill", 0},
    {"main",       MAIN_ARG_USER_FIRST_KEY+6, 0, 0, "main fim, default is proxy", 0},
    {"corelimit",  MAIN_ARG_USER_FIRST_KEY+7, "LIMIT", 0, "core file max size, format: number[k|M]", 0},
    { 0,0,0,0,0,0 }
};

#define CORE_DUMPS_MAX (128*1024*1024)

static struct fim_options foptions =
{
    .vmajor             = 0,
    .vminor             = 2,
    .timestamp          = true,
    .default_log_level  = LOG_INFO,
    .default_stdout_msg = 0,
    .local_fd           = 0,
    .scheduling         = SCHED_OTHER,
    .prio_max           = 0,
    .prio_min           = 0,
    .corelimit          = CORE_DUMPS_MAX,
    .fd_path            = (char *)COMM_FIM,
    .cfile              = (char *)DEFAULT_CONFIG_FILE,
    .status             = false,
    .terminate          = false,
    .dontkill           = false,
    .main               = false,
    .start_level        = MIN_RUNLEVEL,
    .current_level      = MIN_RUNLEVEL,
    .run_level          = -1,
};

struct fim_options * mv = &foptions;

/*
//|--------------------------------------------------------------------------
//| Method     : parse_opt
//|--------------------------------------------------------------------------
*/
static error_t
parse_opt(int key, char* arg, struct argp_state *state)
{
    /* Get the INPUT argument from `argp_parse', which we
       know is a pointer to our structure. */
    /*lint --e{613} suppress "state" is not NULL check */
    /*lint --e{668} suppress "arg" is not NULL check */
    struct fim_options *arguments = (struct fim_options *)(state->input);

    switch (key)
    {
        case MAIN_ARG_USER_FIRST_KEY:
            arguments->status = true;
            break;

        case MAIN_ARG_USER_FIRST_KEY+1:
            arguments->terminate = true;
            break;

        case MAIN_ARG_USER_FIRST_KEY+2:
            arguments->cfile = arg;
            break;

        case MAIN_ARG_USER_FIRST_KEY+3:
            arguments->start_level = strtoul(arg, NULL, 0);
            break;

        case MAIN_ARG_USER_FIRST_KEY+4:
            arguments->run_level = strtoul(arg, NULL, 0);
            break;

        case MAIN_ARG_USER_FIRST_KEY+5:
            arguments->dontkill = true;
            break;

        case MAIN_ARG_USER_FIRST_KEY+6:
            arguments->main = true;
            break;

        case MAIN_ARG_USER_FIRST_KEY+7:
        {
            char * unit;
            unsigned long weight;
      
            if( (unit = strchr(arg, 'M')) )
            {
                *unit = '\0';
                weight = 1024*1024;
            }
            else if( (unit = strchr(arg, 'k')) )
            {
                *unit = '\0';
                weight = 1024;
            }
            else
            {
                weight = 1;
            }
            arguments->corelimit = strtoul(arg, NULL, 0) * weight;
            break;

        }

        case MAIN_ARG_TIMESTAMP_KEY:
            arguments->timestamp = false;
            break;

        case MAIN_ARG_VERBOSE_KEY:
            arguments->default_log_level  = LOG_DEBUG;
            arguments->default_stdout_msg = LOG_PERROR;
            break;

        case MAIN_ARG_SCHEDULING_KEY:
            if( strcmp(arg, "FIFO") == 0 )
            {
                arguments->scheduling = SCHED_FIFO;
            }
            else if( strcmp(arg,"RR") == 0 )
            {
                arguments->scheduling = SCHED_RR;
            }
            break;

        case MAIN_ARG_PRIO_MAX_KEY:
            arguments->prio_max = strtoul(arg, NULL, 0);
            break;

        case MAIN_ARG_PRIO_MIN_KEY:
            arguments->prio_min = strtoul(arg, NULL, 0);
            break;

        case MAIN_ARG_FD_KEY :
            arguments->fd_path = arg;
            break;

        case ARGP_KEY_ARG:
            return -EINVAL;
            break;

        case ARGP_KEY_END:
            break;

        default:
            return ARGP_ERR_UNKNOWN;
    }
    return 0;
}

static struct argp argp = { fim_argp_options, parse_opt, NULL, NULL, 0, 0, 0 };

/*
//|--------------------------------------------------------------------------
//| Method     : main
//|--------------------------------------------------------------------------
*/
int main
(
  int    argc,
  char * argv[]
)
{
    /* force the fim run on CORE0 */
/*    {
        unsigned long cpuSet = 1;
        if( sched_setaffinity(0, sizeof(cpuSet), (cpu_set_t*)&cpuSet) != 0 )
        {
            fprintf(stderr, "failed to set fim's cpu affinity to core0 \n");
            return -1;
        }
        fprintf(stdout, "set fim's cpu affinity to core0 \n");
    }*/

    /*lint --e{613} suppress "argv" is not NULL check */
    if( argp_parse (&argp, argc, (char **)argv, 0, 0, mv) )
    {
        printf("argp_parse faield %m\n");
    }

    //openlog(argv[0], mv->default_stdout_msg | LOG_PID | LOG_NDELAY, LOG_USER);
    //(void)setlogmask(LOG_UPTO(mv->default_log_level));

    if( mv->main )
    {
        int dont_close_outputs = access("/mnt/usr/.config/fim_silent", F_OK);
        if( daemon(1, dont_close_outputs) )
        {
            printf("can't fork fim %m\n");
	        return -1;
        }

        return fim_main(mv);
    }
    else
    {
        return fimp_main(mv);
    }
}
/*lint -restore */
